{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Setup\n",
    "\n",
    "First, import the necessary modules from QGL. This will also import ``numpy`` into the namespace as ``np``. \n",
    "\n",
    "The AWGDir environment variable is used to indicate where QGL will store it's output sequence files. First we load the QGL module. It defaults to a temporary directory as provided by Python's tempfile module.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from QGL import *"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next we instantiate the channel library. By default bbndb will use an sqlite database at the location specified by the BBN_DB environment variabe, but we override this behavior below in order to use a temporary in memory database for testing purposes.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "?RabiAmp"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cl = ChannelLibrary(db_resource_name=\":memory:\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The channel library has a number of convenience functions defined to create instruments and qubits, as well as functions to define the relationships between them. Let us create a qubit first:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q1 = cl.new_qubit(\"q1\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In order to compile the QGL program into pulse sequences, we need to define a minimal hardware configuration. Basically, we need to specify AWG resources for output pulse compilation and digitizer resources for signal measurement.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Most calls required label and address. Let's define \n",
    "# an AWG for control pulse generation \n",
    "aps2_1 = cl.new_APS2(\"BBNAPS1\", address=\"192.168.5.101\") \n",
    "# an AWG for measurement pulse generation\n",
    "aps2_2 = cl.new_APS2(\"BBNAPS2\", address=\"192.168.5.102\")\n",
    "# and digitizer for measurement collection\n",
    "dig_1  = cl.new_X6(\"X6_1\", address=0)\n",
    "\n",
    "# Qubit q1 is controlled by AWG aps2_1\n",
    "cl.set_control(q1, aps2_1)\n",
    "# Qubit q1 is measured by AWG aps2_2 and digitizer dig_1\n",
    "cl.set_measure(q1, aps2_2, dig_1.ch(1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Basic sequence construction and plotting"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can construct simple gate sequences by creating `Lists` of `Pulse` objects. These can be constructed by calling various primitives defined for qubits, for example, 90 and 180 degree rotations about X and Y:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "seq1 = [[X(q1), Y(q1)]]\n",
    "seq2 = [[X90(q1),Y90(q1),X(q1),Id(q1),Y(q1)]]\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This sequence of pulses can be plotted for visual review. First, you must compile the QGL into pulses based on the hardware defined above. Since our `Qubit` object is a quadrature channel, you see two colors corresponding to the I and Q control signals."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mf = compile_to_hardware(seq1, 'Test1')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot_pulse_files(mf)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, let's plot the second sequence. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mf = compile_to_hardware(seq2, 'Test2')\n",
    "plot_pulse_files(mf)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Constructing more sophisticated sequences"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To get rotations of arbitrary angle (i.e., amplitude control of the pulse), you can use the \"theta\" primitive:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "seq = [[Xtheta(q1, 0.2), Xtheta(q1, 0.4), Xtheta(q1, 0.6), Xtheta(q1, 0.8), Xtheta(q1, 1.0)]]\n",
    "mf = compile_to_hardware(seq, 'Test')\n",
    "plot_pulse_files(mf)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To rotate about an arbitrary axis, use the \"U\" primitives:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "seq = [[U(q1, 0.0), U(q1, np.pi/8), U(q1, np.pi/4), U(q1, 3*np.pi/8), U(q1, np.pi/2)]]\n",
    "mf = compile_to_hardware(seq, 'Test')\n",
    "plot_pulse_files(mf)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Z rotations are performed in \"software:\" they act as frame changes on the following pulses."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "seq = [[X(q1), Z90(q1), X(q1), Z90(q1), X(q1), Z90(q1), X(q1), Z90(q1), X(q1)]]\n",
    "mf = compile_to_hardware(seq, 'Test')\n",
    "plot_pulse_files(mf)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Sequences can act on multiple qubits, i.e., channels. Let's create another \"logical\" qubit channel as well as a \"physical\" channel."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q2 = cl.new_qubit(\"q2\")\n",
    "aps2_3 = cl.new_APS2(\"BBNAPS3\", address=\"192.168.5.103\")\n",
    "cl.set_control(q2, aps2_3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When you plot a sequence with multiple logical channels, each channel (both I and Q) is plotted seperately."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "seq = [[X(q1), X(q2), Y(q1), Y(q2)]]\n",
    "mf = compile_to_hardware(seq, 'Test')\n",
    "plot_pulse_files(mf)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "One can express simultaneous operations with the `*` operator (meant to evoke a tensor product). If no operation is specified for a channel in a given time slot, an identity (no-op) operation is inserted."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "seq = [[X(q1)*X(q2), X(q1)*Y(q2), Y(q1), X(q2)]]\n",
    "mf = compile_to_hardware(seq, 'Test')\n",
    "plot_pulse_files(mf)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Constructing sequences with measurements\n",
    "\n",
    "Measurement pulses can be created with the `MEAS` primitive. Given a qubit X, the compiler finds the associated logical measurement channel and creates a `Pulse` on that channel. Note that a `Trigger` pulse for the digitizer is created along with the qubit measurement pulse. \n",
    "\n",
    "Remember, measurement channel was defined above with:   \n",
    "Qubit q1 is measured by AWG aps2_2 and digitizer dig_1   \n",
    "`cl.set_measure(q1, aps2_2, dig_1.ch(1))`   "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "seq = [[MEAS(q1)]]\n",
    "mf = compile_to_hardware(seq, 'Test')\n",
    "plot_pulse_files(mf)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Single-sideband modulation\n",
    "\n",
    "In order to prevent leakage at the source frequency, typically single-sideband (SSB) modulation with IQ mixers is used to generate both control and measurement pulses. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For control pulses, we define the SSB frequency as\n",
    "`q1.frequency`. In the following example, you can set that parameter to a non-zero SSB to modulate the pulse envelope. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q1.frequency = 50e6\n",
    "seq= [[X(q1), Y(q1)]]\n",
    "mf = compile_to_hardware(seq, 'Test')\n",
    "plot_pulse_files(mf)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For measurement pulses, we define the SSB frequency as \n",
    "\n",
    "`q1.measure_chan.autodyne_frequency`\n",
    "\n",
    "Note that `autodyne_frequency` is different from `frequency`. In the first case, the SSB frequency is baked directly in the pulse waveform, and is independent of the time when the pulse is applied. In the second case (normally used for the control SSB, see above), the actual pulse shape depends on the time when the pulse occurs. This is in order to maintain the phase reference allowing for arbitrary rotations around any transversal axis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# set the modulation frequency\n",
    "cl[\"q1\"].measure_chan.autodyne_frequency = 10e6\n",
    "\n",
    "seq = [[X(q1), MEAS(q1)]]\n",
    "mf = compile_to_hardware(seq, 'Test')\n",
    "plot_pulse_files(mf)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Longer sequences using list comprehensions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Rabi amplitude\n",
    "Note the increasing amplitude with each sequence. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "seq = [[Xtheta(q1, a), MEAS(q1)] for a in np.linspace(0,2,11)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mf = compile_to_hardware(seq, 'Test')\n",
    "plot_pulse_files(mf)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### T1\n",
    "Note the delay between the X pulse and the measurement. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "seq = [[X(q1), Id(q1, d), MEAS(q1)] for d in np.linspace(0, 10e-7, 11)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mf = compile_to_hardware(seq, 'Test')\n",
    "plot_pulse_files(mf)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ramsey\n",
    "Note the varying delay between the X pulses prior to measurement. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "seq = [[X90(q1), Id(q1, delay), X90(q1), MEAS(q1)] for delay in np.linspace(0, 5e-7, 11)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mf = compile_to_hardware(seq, 'Test')\n",
    "plot_pulse_files(mf)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
